﻿/// <summary>
/// 
/// </summary>
using System.Collections.Generic;
using System;
using System.Data;
using Framework.Data.Exceptions;
namespace Framework.Data.Core
{
    
    public class AttachedContainer : IEntityCollection<object, IBaseEntity>, IContainer
    {

        #region "Membres"
        //Type des entités du container
        protected System.Type _entitiesType;
        //Filtre sur la collection
        protected List<string> _filters = new List<string>();
        //Le parent
        protected IBaseEntity _parent = null;
        //Nom de shema de la table de lien
        protected string _linkSchemaName = "GCL";
        //Nom de la table de lien
        protected string _linkTableName = string.Empty;
        //Liste des entités attachés
        protected EntityCollection<object, IBaseEntity> _entityCollection;
        //Indique si la collection est déjà loader

        protected bool _isLoaded = false;
        #endregion

        #region "Constructeurs"

        /// <summary>
        /// Constructeur
        /// </summary>
        /// <param name="entitiesType">Types des entités</param>
        /// <param name="parent">L'entité parent</param>
        /// <remarks></remarks>
        public AttachedContainer(System.Type entitiesType, IBaseEntity parent, string linkTableName)
        {
            this._entitiesType = entitiesType;
            this._entityCollection = new EntityCollection<object, IBaseEntity>(EntityType);
            this._parent = parent;
            this._linkTableName = linkTableName;
        }
        #endregion

        #region "Propriétés"

        public System.Type EntitiesType
        {
            get { return this._entitiesType; }
            set { this._entitiesType = value; }
        }

        public List<string> Filters
        {
            get { return this._filters; }
        }

        public IBaseEntity Parent
        {
            get { return this._parent; }
            set { this._parent = value; }
        }

        public string LinkShemaName
        {
            get { return this._linkSchemaName; }
            set { this._linkSchemaName = value; }
        }

        public string LinkTableName
        {
            get { return this._linkTableName; }
            set { this._linkTableName = value; }
        }

        #endregion

        #region "IContainer"

        /// <summary>
        /// Fonction qui permet de créer un entité
        /// </summary>
        /// <returns>IBaseEntity</returns>
        /// <remarks>Cette fonction n'est pas implémenté dans cette classe.</remarks>
        public IBaseEntity CreateEntity(IDbConnection connection = null )
        {
            throw new NotImplementedException("Vous ne pouvez pas créer un entité à partir d'un AttachContainer");
        }

        /// <summary>
        /// Fonction qui permet de dupliquer un entité
        /// </summary>
        /// <param name="entity">L'entité à dupliquer</param>
        /// <returns>IBaseEntity</returns>
        /// <remarks></remarks>
        public IBaseEntity DuplicateEntity(IBaseEntity entity, IDbConnection connection = null )
        {
            this.AddEx(entity.GetPrimaryKeyField().Value, entity);
            return entity;
        }

        public IEntityCollection<object, IBaseEntity> EntityCollection
        {
            get { return this._entityCollection; }
        }

        public System.Type EntityType
        {
            get { return this._entitiesType; }
        }

        public bool IsLoaded
        {
            get { return this._isLoaded; }
            set { this._isLoaded = value; }
        }

        /// <summary>
        /// Procédure de chargement de la liste
        /// </summary>
        /// <remarks></remarks>
        public void LoadData(IDbConnection connection = null, Int32 fetchRowCount = 250, Int32 rowsPerPage = 0, Int32 pageIndex = 0)
        {
            this.LoadData(false, connection, fetchRowCount, rowsPerPage, pageIndex);
        }

        /// <summary>
        /// Procédure qui charge les données
        /// </summary>
        /// <param name="forceReload">Indique si on doit forcer le reload</param>
        /// <remarks></remarks>
        public void LoadData(bool forceReload, IDbConnection connection = null, Int32 fetchRowCount = 250, Int32 rowsPerPage = 0, Int32 pageIndex = 0)
        {
            if ((!this._isLoaded) | (forceReload))
            {
                //Set le filtre sur le parent
                List<string> filterList = new List<string>();
                filterList.AddRange(this._filters);
                filterList.Add(" AND " + ((IEntityField)this.Parent.GetPrimaryKeyField()).TableName + "." + ((IEntityField)this.Parent.GetPrimaryKeyField()).DatabaseFieldName + " = " + this._linkTableName + "." + ((IEntityField)this.Parent.GetPrimaryKeyField()).DatabaseFieldName);
                filterList.Add(" AND " + this._linkTableName + "." + ((IEntityField)this.Parent.GetPrimaryKeyField()).DatabaseFieldName + " = " + this.Parent.GetPrimaryKeyField().OriginalValue.ToString());
                IBaseEntity attachedEntity = (IBaseEntity)Activator.CreateInstance(this._entitiesType, new object[] { connection });

                //Set le trie
                string sortOrder = null;
                if ((this._entityCollection.SortOrder == null) || (this._entityCollection.SortOrder == string.Empty))
                {
                    sortOrder = attachedEntity.GetDefaultSortOrder();
                }
                else
                {
                    sortOrder = this._entityCollection.SortOrder;
                }

                this._entityCollection = (EntityCollection<object,IBaseEntity>)BaseEntity.GetAttachedEntityList(this._entitiesType, this._parent, this._linkSchemaName, this._linkTableName, filterList, sortOrder, connection, fetchRowCount, rowsPerPage, pageIndex);
            }
            this._isLoaded = true;
        }

        /// <summary>
        /// Fonction qui retire une entité de la liste
        /// </summary>
        /// <param name="entity">L'entité à retirer</param>
        /// <returns>Boolean</returns>
        /// <remarks></remarks>
        public bool RemoveEntity(IBaseEntity entity)
        {
            return this._entityCollection.Remove(entity.GetPrimaryKeyField().Value);
        }

        /// <summary>
        /// Fonction de sauvegarde de la collection
        /// </summary>
        /// <returns>Boolean</returns>
        /// <remarks>Cette fonction n'est pas implémenté dans cette classe.</remarks>
        public bool Save(IDbConnection connection = null)
        {
            throw new NotImplementedException("Vous ne pouvez pas sauvegarder à partir d'un AttachContainer");
        }

        /// <summary>
        /// Fonction qui permet de valider un entité
        /// </summary>
        /// <returns>List(Of AppException)</returns>
        /// <remarks></remarks>
        public virtual List<AppException> ValidateRules()
        {
            return new List<AppException>();
        }

        /// <summary>
        /// Fonction qui permet de savoir s'il y a eu des changements à la liste
        /// </summary>
        /// <returns>Boolean</returns>
        /// <remarks></remarks>
        public virtual bool IsDirty()
        {
            foreach (AttachedEntity entity in this._entityCollection.Values)
            {
                if (entity.IsDirty())
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Procédure qui applique les modifications aux entités
        /// </summary>
        /// <remarks></remarks>
        public virtual void AcceptChanges()
        {
            List<IBaseEntity> entitiesToRemove = new List<IBaseEntity>();
            foreach (IBaseEntity entity in this._entityCollection.Values)
            {
                if (entity.MarkAsDeleted)
                {
                    entitiesToRemove.Add(entity);
                }
                else
                {
                    entity.AcceptChanges();
                }
            }
            foreach (IBaseEntity entity in entitiesToRemove)
            {
                this.RemoveEntity(entity);
            }
        }

        /// <summary>
        /// Procédure qui rejette les modifications aux entités
        /// </summary>
        /// <remarks></remarks>
        public virtual void RejectChanges()
        {
            List<IBaseEntity> entitiesToRemove = new List<IBaseEntity>();
            foreach (IBaseEntity entity in this._entityCollection.Values)
            {
                if (entity.IsNew)
                {
                    entitiesToRemove.Add(entity);
                }
                else
                {
                    entity.RejectChanges();
                }
            }
            foreach (IBaseEntity entity in entitiesToRemove)
            {
                this.RemoveEntity(entity);
            }
        }

        #endregion

        #region "IEntityCollection"

        /// <summary>
        /// Fonction qui permet de convertir la collection en Liste d'objet
        /// </summary>
        /// <param name="displayFieldName">Le champ à utiliser</param>
        /// <returns>ListObject()</returns>
        /// <remarks></remarks>
        public ListObject[] ToListObject(string displayFieldName)
        {
            return this._entityCollection.ToListObject(displayFieldName);
        }

        /// <summary>
        /// Fonction qui permet de convertir la collection en Liste d'objet
        /// </summary>
        /// <param name="displayFieldName">Le champ à utiliser</param>
        /// <param name="sortList">Indique si la liste doit être trié par le DisplayField</param>
        /// <returns>ListObject()</returns>
        /// <remarks></remarks>
        public ListObject[] ToListObject(string displayFieldName, bool sortList)
        {
            return this._entityCollection.ToListObject(displayFieldName, sortList);
        }

        /// <summary>
        /// Fonction qui converti la collection en IList
        /// </summary>
        /// <param name="displayFieldsName">Champs qui sera affiché séparé par une virgule. Ex: "Nom, Description"</param>
        /// <param name="seperator">Chaîne utilisé pour séparé les champs lors de l'affichage</param>
        /// <param name="sortList">Indique si la liste doit être triée</param>
        /// <returns>IList</returns>
        /// <remarks></remarks>
        public ListObject[] ToListObject(string displayFieldsName, string seperator, bool sortList)
        {
            return this._entityCollection.ToListObject(displayFieldsName, seperator, sortList);
        }

        /// <summary>
        /// Procédure d'ajout d'un entité dans la collection
        /// </summary>
        /// <param name="item">Item à ajouter</param>
        /// <remarks></remarks>
        public void Add(System.Collections.Generic.KeyValuePair<object, IBaseEntity> item)
        {
            this._entityCollection.Add(item.Key, new AttachedEntity(item.Value));
        }

        /// <summary>
        /// Procédure qui retire tout les éléments de la collection
        /// </summary>
        /// <remarks></remarks>
        public void Clear()
        {
            this._entityCollection.Clear();
        }

        /// <summary>
        /// Fonction qui indique si la collection contient l'élément
        /// </summary>
        /// <param name="item">Item à rechercher</param>
        /// <returns>Boolean</returns>
        /// <remarks></remarks>
        public bool Contains(System.Collections.Generic.KeyValuePair<object, IBaseEntity> item)
        {
            return this._entityCollection.ContainsKey(item.Key);
        }

        /// <summary>
        /// Procédure qui copie les éléments dans la collection dans un array
        /// </summary>
        /// <param name="array">Array</param>
        /// <param name="arrayIndex">Indexe de départ</param>
        /// <remarks>Cette procédure n'est pas implémenté</remarks>
        public void CopyTo(System.Collections.Generic.KeyValuePair<object, IBaseEntity>[] array, int arrayIndex)
        {
            throw new NotImplementedException("Vous ne pouvez pas faire un CopyTo à partir d'un AttachContainer");
        }

        public int Count
        {
            get { return this._entityCollection.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        /// <summary>
        /// Fonction qui permet de retirer un élément de la collection
        /// </summary>
        /// <param name="item">Item à retirer</param>
        /// <returns>Boolean</returns>
        /// <remarks></remarks>
        public bool Remove(System.Collections.Generic.KeyValuePair<object, IBaseEntity> item)
        {
            return this._entityCollection.Remove(item.Key);
        }

        /// <summary>
        /// Procédure d'ajout d'un élément dans la collection
        /// </summary>
        /// <param name="key">Clé</param>
        /// <param name="value">Valeur</param>
        /// <remarks></remarks>
        public void AddEx(object key, IBaseEntity value)
        {
            this._entityCollection.Add(key, new AttachedEntity(value));
        }
        void System.Collections.Generic.IDictionary<object, IBaseEntity>.Add(object key, IBaseEntity value)
        {
            AddEx(key, value);
        }

        /// <summary>
        /// Fonction qui indique si la clé existe dans la collection
        /// </summary>
        /// <param name="key">Clé</param>
        /// <returns>Boolean</returns>
        /// <remarks></remarks>
        public bool ContainsKey(object key)
        {
            return this._entityCollection.ContainsKey(key);
        }

        public IBaseEntity this[object key]
        {
            get { return this.EntityCollection[key]; }
            set { this.EntityCollection[key] = value; }
        }

        public System.Collections.Generic.ICollection<object> Keys
        {
            get { return this._entityCollection.Keys; }
        }

        /// <summary>
        /// Fonction qui permet de retirer un élément de la collection
        /// </summary>
        /// <param name="key">Clé</param>
        /// <returns>Boolean</returns>
        /// <remarks></remarks>
        public bool RemoveEx(object key)
        {
            return this._entityCollection.Remove(key);
        }
        bool System.Collections.Generic.IDictionary<object, IBaseEntity>.Remove(object key)
        {
            return RemoveEx(key);
        }

        /// <summary>
        /// Fonction qui tente de lire une valeur dans la collection
        /// </summary>
        /// <param name="key">Clé</param>
        /// <param name="value">Valeur</param>
        /// <returns>Boolean</returns>
        /// <remarks></remarks>
        public bool TryGetValue(object key, out IBaseEntity value)
        {
            return this.EntityCollection.TryGetValue(key, out value);
        }

        public System.Collections.Generic.ICollection<IBaseEntity> Values
        {
            get { return this.EntityCollection.Values; }
        }

        /// <summary>
        /// Fonction qui permet de retourner un Énumérateur de la collection
        /// </summary>
        /// <returns>IEnumerator</returns>
        /// <remarks></remarks>
        public System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<object, IBaseEntity>> GetEnumerator()
        {
            return this._entityCollection.GetEnumerator();
        }

        /// <summary>
        /// Fonction qui permet de retourner un Énumérateur de la collection
        /// </summary>
        /// <returns>IEnumerator</returns>
        /// <remarks></remarks>
        public System.Collections.IEnumerator GetEnumeratorEx()
        {
            return this._entityCollection.GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumeratorEx();
        }

        /// <summary>
        /// Fonction qui copy la collection dans un array list
        /// </summary>
        /// <returns>List(Of IBaseEntity)</returns>
        /// <remarks></remarks>
        public System.Collections.Generic.List<IBaseEntity> ToArrayList()
        {
            return this._entityCollection.ToArrayList();
        }

        public string SortOrder
        {
            get { return this._entityCollection.SortOrder; }
            set { this._entityCollection.SortOrder = value; }
        }


        #endregion

    }
}